package com.enation.app.javashop.core.promotion.luck.utils;

import java.math.BigDecimal;
import java.util.*;

/**
 * 概率计算工具类
 */
public class ProbabilityUtils {

    private final double[] probability;
    private final int[] alias;
    private final int length;
    private final Random rand;

    public ProbabilityUtils(List<Double> prob) {
        this(prob, new Random());
    }

    public ProbabilityUtils(List<Double> probList, Random rand) {
        validate(probList);

        this.rand = rand;
        this.length = probList.size();
        this.probability = new double[length];
        this.alias = new int[length];

        double[] probTemp = new double[length];
        Deque<Integer> small = new ArrayDeque<>();
        Deque<Integer> large = new ArrayDeque<>();

        for (int i = 0; i < length; i++) {
            probTemp[i] = probList.get(i) * length;
            if (probTemp[i] < 1.0)
                small.add(i);
            else
                large.add(i);
        }

        while (!small.isEmpty() && !large.isEmpty()) {
            int less = small.pop();
            int more = large.pop();
            probability[less] = probTemp[less];
            alias[less] = more;
            probTemp[more] = probTemp[more] - (1.0 - probability[less]);
            if (probTemp[more] < 1.0){
                small.add(more);
            }else{
                large.add(more);
            }
        }

        while (!small.isEmpty()){
            probability[small.pop()] = 1.0;
        }
        while (!large.isEmpty()){
            probability[large.pop()] = 1.0;
        }
    }

    public int next() {
        int column = rand.nextInt(length);
        boolean coinToss = rand.nextDouble() < probability[column];
        return coinToss ? column : alias[column];
    }


    public static void validate(List<Double> probList){
        if (probList == null){
            throw new NullPointerException();
        }
        if (probList.size() == 0){
            throw new IllegalArgumentException("概率列表不能为空");
        }
        BigDecimal totalProbability = BigDecimal.ZERO;
        for (Double p : probList) {
            totalProbability = totalProbability.add(BigDecimal.valueOf(p));
        }
        if(totalProbability.compareTo(BigDecimal.ONE) != 0){
            throw new IllegalArgumentException("概率分布总和不是1，请重新配置");
        }
    }

    /* 概率测试 */
    public static void main(String[] argv) {
        String[] str = {"一等奖", "二等奖", "三等奖", "四等奖", "未中奖"};
        for (int i = 0; i < 1000; i++) {
            List<Double> prob = new ArrayList<>();
            prob.add(0.0);
            prob.add(0.0);
            prob.add(0.0);
            prob.add(0.1);
            prob.add(1.0);
            ProbabilityUtils am = new ProbabilityUtils(prob);
            System.out.println(str[am.next()]);
        }
    }
}